/*
 * Copyright 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
@file:Suppress("DEPRECATION")

package com.tencent.kuikly.compose.ui.node

import androidx.annotation.RestrictTo
import androidx.compose.runtime.Applier
import com.tencent.kuikly.compose.ui.focus.FocusOwner
import com.tencent.kuikly.compose.ui.graphics.Canvas
import com.tencent.kuikly.compose.ui.input.InputModeManager
import com.tencent.kuikly.compose.ui.layout.Placeable
import com.tencent.kuikly.compose.ui.layout.PlacementScope
import com.tencent.kuikly.compose.ui.modifier.ModifierLocalManager
import com.tencent.kuikly.compose.ui.platform.KuiklySoftwareKeyboardController
import com.tencent.kuikly.compose.ui.platform.ViewConfiguration
import com.tencent.kuikly.compose.ui.unit.Constraints
import com.tencent.kuikly.compose.ui.unit.Density
import com.tencent.kuikly.compose.ui.unit.LayoutDirection
import com.tencent.kuikly.core.base.DeclarativeBaseView
import kotlin.coroutines.CoroutineContext

/**
 * Owner implements the connection to the underlying view system. On Android, this connects
 * to Android [views][android.view.View] and all layout, draw, input, and accessibility is hooked
 * through them.
 */
internal interface Owner {

    /**
     * The root layout node in the component tree.
     */
    val root: KNode<DeclarativeBaseView<*, *>>

    /**
     * Draw scope reused for drawing speed up.
     */
    val sharedDrawScope: LayoutNodeDrawScope

    val rootForTest: RootForTest

//    /**
//     * Provide haptic feedback to the user. Use the Android version of haptic feedback.
//     */
//    val hapticFeedBack: HapticFeedback

    /**
     * Provide information about the current input mode, and a way to programmatically change the
     * input mode.
     */
    val inputModeManager: InputModeManager

//    /**
//     * Provide clipboard manager to the user. Use the Android version of clipboard manager.
//     */
//    val clipboardManager: ClipboardManager
//
//    /**
//     * Provide accessibility manager to the user. Use the Android version of accessibility manager.
//     */
//    val accessibilityManager: AccessibilityManager
//
//    /**
//     * Provide access to a GraphicsContext instance used to create GraphicsLayers for providing
//     * isolation boundaries for rendering portions of a Composition hierarchy as well as for
//     * achieving certain visual effects like masks and blurs
//     */
//    val graphicsContext: GraphicsContext
//
//    /**
//     * Provide toolbar for text-related actions, such as copy, paste, cut etc.
//     */
//    val textToolbar: TextToolbar

//    /**
//     *  A data structure used to store autofill information. It is used by components that want to
//     *  provide autofill semantics.
//     *  TODO(ralu): Replace with SemanticsTree. This is a temporary hack until we have a semantics
//     *  tree implemented.
//     */
//    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
//    @get:ExperimentalComposeUiApi
//    @ExperimentalComposeUiApi
//    val autofillTree: AutofillTree
//
//    /**
//     * The [Autofill] class can be used to perform autofill operations. It is used as a
//     * CompositionLocal.
//     */
//    @Suppress("OPT_IN_MARKER_ON_WRONG_TARGET")
//    @get:ExperimentalComposeUiApi
//    @ExperimentalComposeUiApi
//    val autofill: Autofill?

    val density: Density

//    val textInputService: TextInputService
//
    val softwareKeyboardController: KuiklySoftwareKeyboardController
//
//    val pointerIconService: PointerIconService

    /**
     * Provide a focus owner that controls focus within Compose.
     */
    val focusOwner: FocusOwner

//    /**
//     * Provide information about the window that hosts this [Owner].
//     */
//    val windowInfo: WindowInfo

//    @Deprecated(
//        "fontLoader is deprecated, use fontFamilyResolver",
//        replaceWith = ReplaceWith("fontFamilyResolver")
//    )
//    @Suppress("DEPRECATION")
//    val fontLoader: Font.ResourceLoader
//
//    val fontFamilyResolver: FontFamily.Resolver

    val layoutDirection: LayoutDirection

    /**
     * `true` when layout should draw debug bounds.
     */
    var showLayoutBounds: Boolean
        @RestrictTo(RestrictTo.Scope.LIBRARY)
        @InternalCoreApi
        set

    /**
     * Called by [LayoutNode] to request the Owner a new measurement+layout. [forceRequest] defines
     * whether the node should bypass the logic that would reject measure requests, and therefore
     * force the measure request to be evaluated even when it's already pending measure.
     *
     * [affectsLookahead] specifies whether this measure request is for the lookahead pass.
     */
    fun onRequestMeasure(
        layoutNode: LayoutNode,
        affectsLookahead: Boolean = false,
        forceRequest: Boolean = false,
        scheduleMeasureAndLayout: Boolean = true
    )

    /**
     * Called by [LayoutNode] to request the Owner a new layout. [forceRequest] defines
     * whether the node should bypass the logic that would reject relayout requests, and therefore
     * force the relayout request to be evaluated even when it's already pending measure/layout.
     *
     * [affectsLookahead] specifies whether this relayout request is for the lookahead pass
     * pass.
     */
    fun onRequestRelayout(
        layoutNode: LayoutNode,
        affectsLookahead: Boolean = false,
        forceRequest: Boolean = false
    )

    /**
     * Called when graphics layers have changed the position of children and the
     * OnGloballyPositionedModifiers must be called.
     */
    fun requestOnPositionedCallback(layoutNode: LayoutNode)

    /**
     * Called by [LayoutNode] when it is attached to the view system and now has an owner.
     * This is used by [Owner] to track which nodes are associated with it. It will only be
     * called when [node] is not already attached to an owner.
     */
    fun onAttach(node: LayoutNode)

    /**
     * Called by [LayoutNode] when it is detached from the view system, such as during
     * [LayoutNode.removeAt]. This will only be called for [node]s that are already
     * [LayoutNode.attach]ed.
     */
    fun onDetach(node: LayoutNode)

    /**
     * Returns the position relative to the containing window of the [localPosition],
     * the position relative to the [Owner]. If the [Owner] is rotated, scaled, or otherwise
     * transformed relative to the window, this will not be a simple translation.
     */
//    fun calculatePositionInWindow(localPosition: Offset): Offset

    /**
     * Returns the position relative to the [Owner] of the [positionInWindow],
     * the position relative to the window. If the [Owner] is rotated, scaled, or otherwise
     * transformed relative to the window, this will not be a simple translation.
     */
//    fun calculateLocalPosition(positionInWindow: Offset): Offset

    /**
     * Ask the system to provide focus to this owner.
     *
     * @return true if the system granted focus to this owner. False otherwise.
     */
//    fun requestFocus(): Boolean

    /**
     * Iterates through all LayoutNodes that have requested layout and measures and lays them out.
     * If [sendPointerUpdate] is `true` then a simulated PointerEvent may be sent to update pointer
     * input handlers.
     *
     * This method can dispatch ViewTreeObserver events during its execution. Do not call it during
     * a view's onLayout as an associated listener may invoke side effects that may requestLayout
     * during layout, potentially putting the view hierarchy into an invalid state.
     */
    fun measureAndLayout(sendPointerUpdate: Boolean = true)

    /**
     * Measures and lays out only the passed [layoutNode]. It will be remeasured with the passed
     * [constraints].
     *
     * This method can dispatch ViewTreeObserver events during its execution. Do not call it during
     * a view's onLayout as an associated listener may invoke side effects that may requestLayout
     * during layout, potentially putting the view hierarchy into an invalid state.
     */
    fun measureAndLayout(layoutNode: LayoutNode, constraints: Constraints)

    /**
     * Makes sure the passed [layoutNode] and its subtree is remeasured and has the final sizes.
     */
    fun forceMeasureTheSubtree(layoutNode: LayoutNode, affectsLookahead: Boolean = false)

    /**
     * Creates an [OwnedLayer] which will be drawing the passed [drawBlock].
     */
    fun createLayer(
        drawBlock: (Canvas) -> Unit,
        invalidateParentLayer: () -> Unit,
        view: DeclarativeBaseView<*, *>?
    ): OwnedLayer

    /**
     * The semantics have changed. This function will be called when a SemanticsNode is added to
     * or deleted from the Semantics tree. It will also be called when a SemanticsNode in the
     * Semantics tree has some property change.
     */
    fun onSemanticsChange()

    /**
     * The position and/or size of the [layoutNode] changed.
     */
    fun onLayoutChange(layoutNode: LayoutNode)

    fun onZIndexChange(layoutNode: LayoutNode)

//    /**
//     * The position and/or size of an interop view (typically, an android.view.View) has changed. On
//     * Android, this schedules view tree layout observer callback to be invoked for the underlying
//     * platform view hierarchy.
//     */
//    // TODO: Move to InteropViewContainer
//    @InternalComposeUiApi fun onInteropViewLayoutChange(view: InteropView)

    /**
     * The [FocusDirection] represented by the specified keyEvent.
     */

//    fun getFocusDirection(keyEvent: KeyEvent): FocusDirection?

    val measureIteration: Long

    /**
     * The [ViewConfiguration] to use in the application.
     */
    val viewConfiguration: ViewConfiguration

    /**
     * Performs snapshot observation for blocks like draw and layout which should be re-invoked
     * automatically when the snapshot value has been changed.
     */
    val snapshotObserver: OwnerSnapshotObserver

    val modifierLocalManager: ModifierLocalManager

    /**
     * CoroutineContext for launching coroutines in Modifier Nodes.
     */
    val coroutineContext: CoroutineContext

    /**
     * The scope used to place the outermost layout.
     */
    val placementScope: Placeable.PlacementScope
        get() = PlacementScope(this) // default implementation for test owners

    /**
     * Registers a call to be made when the [Applier.onEndChanges] is called. [listener]
     * should be called in [onEndApplyChanges] and then removed after being called.
     */
    fun registerOnEndApplyChangesListener(listener: () -> Unit)

    /**
     * Called when [Applier.onEndChanges] executes. This must call all listeners registered
     * in [registerOnEndApplyChangesListener] and then remove them so that they are not
     * called again.
     */
    fun onEndApplyChanges()

    /**
     * [listener] will be notified after the current or next layout has finished.
     */
    fun registerOnLayoutCompletedListener(listener: OnLayoutCompletedListener)

//    val dragAndDropManager: DragAndDropManager
//
//    /**
//     * Starts a new text input session and suspends until it's closed. For more information see
//     * [PlatformTextInputModifierNode.establishTextInputSession].
//     *
//     * Implementations must ensure that new requests cancel any active request. They must also
//     * ensure that the previous request is finished running all cancellation tasks before starting
//     * the new session, to ensure that no session code overlaps (e.g. using [Job.cancelAndJoin]).
//     */
//    suspend fun textInputSession(
//        session: suspend PlatformTextInputSessionScope.() -> Nothing
//    ): Nothing

    companion object {
        /**
         * Enables additional (and expensive to do in production) assertions. Useful to be set
         * to true during the tests covering our core logic.
         */
        var enableExtraAssertions: Boolean = false
    }

    interface OnLayoutCompletedListener {
        fun onLayoutComplete()
    }
}
